import fs from "fs";
import fsp from "fs/promises";
import path from "path";
import chokidar from "chokidar";
import debounce from "lodash/debounce";
import cloneDeep from "lodash/cloneDeep";
import type { AstroIntegration } from "astro";
import ColorMap from "../../styles/PersonalThemeColor";
import ResultGenerator from "./ResultGenerator";
import pinyin from 'pinyin'
import type { ItemResult } from "@/types/search";

type Liver = keyof typeof ColorMap

let result: ItemResult[] = []

const mdTree: string[] = [];
const LiverCollect = new Set<Liver>()
function createTree(name: string, isfolder: boolean) {
  //生成索引树
  if (isfolder) {
    mdTree.push(`folder:${name}`);
  } else {
    mdTree.push(name);
  }
}

async function createJson(cPath: string) {
  //记得要json格式
  const data = await fsp.readFile(`${cPath}`, "utf-8");
  eachFound(cPath, data);
}

function eachFound(path: string, content: string) {
  path = path.replace(/.mdx?$/, "").replaceAll("\\", "/")
  if (path.includes("content/wiki/")) {
    path = path.split("content/wiki/")[1]
  }
  if (path.startsWith("_")) return;
  // 获取 Liver 集合
  if (path.includes("items")) {
    const pathArr = path.split("/");
    if (pathArr.length === 4)
      LiverCollect.add(pathArr[2].toLowerCase() as Liver);
  }

  const res = ResultGenerator(path, content)
  if (!res) {
    return
  }

  result.push(res)
}

function generateIndexFile(res: ItemResult[]) {
  // 必须克隆一份，否则生成索引直接爆炸
  const cloneRes = cloneDeep(res)
  LiverCollect.forEach((id) => {
    const { cnName } = ColorMap[id as Liver]
    const pinyinRes: string[] = pinyin(cnName, { style: pinyin.STYLE_NORMAL, group: false }).map((item) => item[0])
    cloneRes.push({
      id,
      type: "Liver",
      title: cnName,
      pinyin: pinyinRes.join(""),
      pinyinHead: pinyinRes.map((item) => item[0]).join(""),
      subTitle: `物品一览 - ${cnName}`,
      link: `/wiki/preview#${cnName}`,
    })
  })
  fs.mkdirSync(".cache", { recursive: true })
  fs.writeFileSync(".cache/search.json", JSON.stringify(cloneRes, null, 2))
}

/**
 * Get content search index
 * 
 * **Note:** Usually use this in development, because now we use `pagefind` as core search lib
 * @param cPath content collection path, you should use `src/content` in normal
 * @param lay layer, you should set 0 in normal
 * @param writeFile write result to cache dir for search
 * @returns {Promise<ItemResult[]>}
 */
export async function rc(cPath: string, lay = 0, writeFile = true): Promise<ItemResult[]> {
  result = []
  const files = await fsp.readdir(cPath /*content的位置*/);
  await Promise.all(files.map(async (fName) => {
    if (fs.lstatSync(path.join(cPath, fName)).isDirectory()) {
      createTree(fName, true);
      await rc(path.join(cPath, fName), lay + 1);
    } else if (
      path.extname(fName).toLowerCase() === ".md" ||
      path.extname(fName).toLowerCase() === ".mdx"
    ) {
      createTree(fName, false);
      await createJson(path.join(cPath, fName));
    }
  }))
  if (lay === 0 && writeFile)
    generateIndexFile(result)
  return result
}

const debounceRc = debounce(rc, 500);

const contentPath = `src/content`

export default function (): AstroIntegration {
  return {
    name: "BuildSearchIndex",
    hooks: {
      "astro:server:start": async () => {
        // 开发环境调用，用 chokidar 包监听 src/content/ 下所有 md 文件变化，生成索引
        //用 chokidar 包监听 src/content/ 下所有 md 文件变化
        //fs.readdirSync / glob.sync（额外依赖） 递归遍历文件夹中后缀为.md的文件（是否content中各处都可能会有md？）
        //fs.filename（__dirname + '/名字'）获取当前文件名字，递归时设置flag数值++表示深度，按深度放入索引对象（或者每一级直接将文件夹名作为属性名，具体看结构）
        chokidar
          .watch(contentPath, {
            persistent: true,
            // ignored: /(^|[\/\\])\../, // 忽略点文件
            // cwd: '.', // 表示当前目录
            depth: 99, // 监听深度
          })
          .on("all", (_, path) => {
            if (path.endsWith(".md") || path.endsWith(".mdx"))
              debounceRc(contentPath)
          })
      },
      "astro:build:start": async () => {
        // 打包开始调用，生成 json 文件并存储到 .cache 目录下
        debounceRc(contentPath)
      },
    },
  };
}
